/*************************************************************************
 *************************************************************************
 	 	 	 	 	 	 M2SMA : Engine.cpp
 *************************************************************************
 *
 	 Copyright (C) 2008
 	 Steven Costiou, Julien Yven; Master 2 Complex Systems
 	 University of Western Brittany (UBO), Brest, France
 *
 *************************************************************************
 *************************************************************************
 *
 	 Copyright (C) 2011
 	 M2SMA reworked and simplified core, Steven Costiou, France
 *
 **************************************************************************
 **************************************************************************
 *
    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program.  If not, see <http://www.gnu.org/licenses/
 *
 **************************************************************************
 **************************************************************************/

#include "Engine.h"
#include <stdlib.h>
#include <sstream>
#include <iostream>
#include <fstream>

Engine::Engine()
{
	this->schedulerType = BASIC_SCHEDULER;
	initializeAttributes();
}

Engine::Engine(int aSchedulerType)
{
	this->schedulerType = aSchedulerType;
	initializeAttributes();
}

Engine::~Engine()
{
	delete this->scheduler;
}

void Engine::initializeAttributes()
{
	initializeScheduler();
	this->systemIsRunning = false;
}

void Engine::initializeScheduler()
{
	switch(this->schedulerType)
	{
		case BASIC_SCHEDULER:
			this->scheduler = new Scheduler();
			break;
		case PRIORITY_SCHEDULER:
			this->scheduler = new PriorityScheduler();
			break;
	}
}

bool Engine::isClassNameInClassNamesVector(string aClassName, vector<string>* anAgentClassNames)
{
	for (unsigned int i = 0; i < anAgentClassNames->size(); ++i)
	{
		if(anAgentClassNames->at(i) == aClassName)
			return true;
	}

	return false;
}

/***************************************************************
 * *************************************************************
 * SYSTEM PUBLIC INTERFACE
 * *************************************************************
 **************************************************************/

void Engine::work()
{
	this->scheduler->cycle();
}

void Engine::workForCycles(unsigned int numberOfCycles)
{
	for (unsigned int i = 0; i < numberOfCycles; ++i)
		work();
}

void Engine::workUntilSystemBreakDown()
{
	while(systemIsRunning)
		work();
}

Agent* Engine::getAgent(string aName)
{
	map<string, Agent*>::iterator it;

	it = this->scheduler->getAgentList()->find(aName);

	if (it != this->scheduler->getAgentList()->end())
		return it->second;

	return NULL;
}

set<Agent*>* Engine::getAllAgentOfClass(string aClassName)
{
	map<string, set<Agent*> >* agentClassesList = this->scheduler->getAgentListByClasses();
	set<Agent*>* allAgentsOfClassName = new set<Agent*>();

	map<string, set<Agent*> >::iterator mapIt;
	set<Agent*>::iterator setIt;

	mapIt = agentClassesList->find(aClassName);
	if(mapIt != agentClassesList->end())
	{
		for (setIt = mapIt->second.begin(); setIt != mapIt->second.end(); setIt++)
			allAgentsOfClassName->insert(*setIt);

		return allAgentsOfClassName;
	}

	return allAgentsOfClassName;
}

vector<Agent*>* Engine::getAllAgents()
{
	map<string, Agent*>* agentList = this->scheduler->getAgentList();
	vector<Agent*>* allAgents = new vector<Agent*>();
	map<string, Agent *>::iterator it;

	for (it = agentList->begin(); it != agentList->end(); it++)
		allAgents->push_back((*it).second);

	return allAgents;
}

bool Engine::isKindOf(string aName, string aClassName)
{
	map<string, Agent*>::iterator it;

	Agent* anAgent = getAgent(aName);

	if(anAgent == NULL)
	{
		cout << "WARNING :: the following requested agent does not exist ==> " << aName << endl;
		return false;
	}
	else
	{
		return isClassNameInClassNamesVector(aClassName, anAgent->getClassNames());
	}

	return false;
}

bool Engine::ptrIsKindOf(Agent* anAgent, string aClassName)
{
	if(agentPointerDoesExist(anAgent))
	{
		return isClassNameInClassNamesVector(aClassName, anAgent->getClassNames());
	}

	return false;
}

bool Engine::agentDoesExist(string aName)
{
	if(getAgent(aName) == NULL)
		return false;
	else
		return true;
}

bool Engine::agentPointerDoesExist(Agent* anAgent)
{
	set<Agent*>::iterator it;
	it = this->scheduler->getAgentPtrs()->find(anAgent);

	if (it != this->scheduler->getAgentPtrs()->end())
		return true;
	else
		return false;
}

void Engine::createAgent(Agent* anAgent)
{
	if (anAgent != NULL)
	{
		this->scheduler->newAgent(anAgent);
	}
	/// TODO : exception handling ?
}

void Engine::createAgentWithPriority(Agent* anAgent, short int aPriority)
{
	if (anAgent != NULL)
	{
		if(schedulerType == PRIORITY_SCHEDULER)
			((PriorityScheduler*) scheduler)->addAgentWithPriority(anAgent, aPriority);
		/// TODO : exception handling ?
	}
	/// TODO : exception handling ?
}

void Engine::removeAgent(Agent* anAgent)
{
	if (anAgent != NULL)
	{
		this->scheduler->removeAndClearAgent(anAgent);
		delete anAgent;
	}
}
